package upm.pb4agent.common;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.List;
import java.util.Date;
import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.PB4AgentOntology;
import upm.pb4agent.ontology.RegistrationForm;

public class RegistrationPIC extends Agent {

	// Variable declaration
	private static final long serialVersionUID = 999L;
	private boolean isRegistrationClosed = false;

	private Codec codec = new SLCodec();
	private Ontology ontology = PB4AgentOntology.getInstance();

	private Long registrationTime;

	protected void setup() {

		System.out.println(getLocalName() + ": has entered into the system");

		getContentManager().registerLanguage(codec);
		getContentManager().registerOntology(ontology);

		try {
			DFAgentDescription dfd = new DFAgentDescription();
			ServiceDescription sd = new ServiceDescription();
			sd.setName(this.getName());
			sd.setType(Constant.REGISTRATION_PIC);
			dfd.addServices(sd);
			DFService.register(this, dfd);
			System.out.println(getLocalName() + ": registered in the DF");
			dfd = null;
			sd = null;
			doWait(10000);
		} catch (FIPAException e) {
			e.printStackTrace();
		}

		registrationTime = new Date().getTime();

		addBehaviour(new ProcessingRegistration(this));
	}

	class ProcessingRegistration extends SimpleBehaviour {

		// Variable declaration
		private static final long serialVersionUID = -9999L;

		public ProcessingRegistration(Agent agent) {
			super(agent);
		}

		@Override
		public void action() {

			// Variable declaration
			Long currentTime = new Date().getTime();
			boolean isRegistrationFormValid = true;

			ACLMessage aclMessage = receive(MessageTemplate.and(
					MessageTemplate.MatchLanguage(codec.getName()),
					MessageTemplate.MatchOntology(ontology.getName())));

			if (aclMessage != null) {

				try {
					ContentElement contentElement = null;
					ACLMessage reply = aclMessage.createReply();

					// Wait for a registration request
					if (aclMessage.getPerformative() == ACLMessage.REQUEST) {

						// Processing request
						// Check if registration is already closed
						if (currentTime - registrationTime >= Constant.REGISTRATION_PERIOD) {
							isRegistrationClosed = true;
						} else {

							// Registration is still opened
							try {
								contentElement = getContentManager()
										.extractContent(aclMessage);

								if (contentElement instanceof Action) {
									Action action = (Action) contentElement;
									Concept concept = action.getAction();

									if (concept instanceof RegistrationForm) {
										RegistrationForm registrationForm = (RegistrationForm) concept;

										// Validate the registration form
										isRegistrationFormValid = validateRegistrationForm(registrationForm
												.getDockDesignation());

										if (isRegistrationFormValid == true) {

											// Inform the sender
											reply.setPerformative(ACLMessage.INFORM);
											myAgent.send(reply);
										}
									}
								}
							} catch (CodecException e) {
								e.printStackTrace();
							}
						}

						if (isRegistrationClosed == true
								|| isRegistrationFormValid == false) {

							// Refuse the request
							reply.setPerformative(ACLMessage.REFUSE);
							myAgent.send(reply);
						}
					} else {

						// Message format is wrong
						reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
						myAgent.send(reply);
					}
				} catch (OntologyException oe) {
					oe.printStackTrace();
				}
			} else {

				// No message yet
				block();
			}
		}

		@Override
		public boolean done() {
			return isRegistrationClosed;
		}

		/**
		 * validateRegistrationForm
		 * 
		 * @param dockDesignationList
		 * @return true if the registration form is valid with criteria: there
		 *         is no duplicate rower ID and no duplicate dock
		 */
		private boolean validateRegistrationForm(List dockDesignationList) {

			// Variable declaration
			boolean isValid = true;
			List rowerIDList = new ArrayList();
			List dockNameList = new ArrayList();
			AID rowerID = null;
			Dock dock = null;
			Iterator iterator = dockDesignationList.iterator();

			while (iterator.hasNext() == true) {
				DockDesignation dockDesignation = (DockDesignation) iterator
						.next();
				rowerID = dockDesignation.getRowerID();
				dock = dockDesignation.getDock();

				// Check if the ID is already in the list
				if (rowerIDList.contains(rowerID) == true) {
					isValid = false;
					break;
				} else {
					rowerIDList.add(rowerID);
				}

				// Check if the dock is already in the list
				if (dockNameList.contains(dock.getName()) == true) {
					isValid = false;
					break;
				} else {
					dockNameList.add(dock.getName());
				}
			}
			return isValid;
		}
	}
}
